from __future__ import division 
import numpy as np
import scipy as sp
import pandas as pd

class Item_base_CF:
    def __init__(self,X,all_song_count):
        self.X = X
        self.mu = np.mean(self.X[:,2])
        
        self.ItemsForUser = {}
        self.UsersForItem = {}
        
        for i in range(self.X.shape[0]):
            uid = self.X[i][0]
            i_id = self.X[i][1]
            rat = self.X[i][2]  #都是1，便于累加
            self.UsersForItem.setdefault(i_id,{})
            self.ItemsForUser.setdefault(uid,{})
            
            self.UsersForItem[i_id][uid] = rat
            self.ItemsForUser[uid][i_id] = rat
            
        n_Items = len(self.UsersForItem)
        print(n_Items)
        
        self.similarity = np.zeros((all_song_count,all_song_count),dtype = np.float)
        self.similarity[:,:] = -1
        
    #计算用户 i1和i2之间的相似性，用交集/并集
    def sim_cal(self,i_id1,i_id2):
        if self.similarity[i_id1][i_id2] != -1:
            return self.similarity[i_id1][i_id2]
        
        sameCount = 0
        for item in self.UsersForItem[i_id1]:
            if item in self.UsersForItem[i_id2]:
                sameCount += 1
                
        if(sameCount == 0):
            self.similarity[i_id1][i_id2] = 0
            self.similarity[i_id2][i_id1] = 0
            return 0
        
        count1 = len(self.UsersForItem[i_id1])
        count2 = len(self.UsersForItem[i_id2])
        
        self.similarity[i_id1][i_id2] = sameCount/(count1 + count2 - sameCount)
        self.similarity[i_id2][i_id1] = sameCount/(count1 + count2 - sameCount)
        
        return sameCount/(count1 + count2 - sameCount)
    
    def pred(self,uid,i_id):
        sim_accumulate = 0.0
        rat_acc = 0.0
        
        for item in self.ItemsForUser[uid]:
            sim = self.sim_cal(item,i_id)
            if sim <= 0:
                continue
            
            rat_acc += sim * self.ItemsForUser[uid][item]
            sim_accumulate += sim   #用相似度取平均
        
        if sim_accumulate == 0:
            return self.mu
        
        return rat_acc/sim_accumulate
    
    def test(self,test_X):
        test_X = np.array(test_X)
        output = pd.DataFrame(columns=['user', 'song', 'playcount', 'score', 'rank'])
        sums=0
        print("the test data size is %f" %(test_X.shape[0]))
        
        for i in range(test_X.shape[0]):
            uid = test_X[i][0]
            i_id = test_X[i][1]
            
            self.UsersForItem.setdefault(i_id,{})
            self.ItemsForUser.setdefault(uid,{})
            
            pre = self.pred(uid,i_id)
            output = output.append({'user': uid,'song':i_id,'playcount':test_X[i][2],'score':pre}, ignore_index=True)
            sums += (pre - test_X[i][2]) ** 2
            
        rmse = np.sqrt(sums/test_X.shape[0])
        print("the rmse on test data is %f" %(rmse))
        
        output['rank'] = output['score'].rank(ascending=0, method='first')
        output = output.sort_values(by=['rank'])
        output = output.head(20)
        return output